Completed
Pull Request — master (#11)
by Anton
02:01
created

React.createClass.handleLastPage   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
/**
2
 * # Stateless Pager component
3
 *
4
 * ## Usage
5
 * ```
6
 * <Pager current={3}
7
 *        total={20}
8
 *        visiblePages={5}
9
 *        onPageChanged={this.handlePageChanged}
10
 *        titles={{
11
 *            first:   "First",
12
 *            prev:    "Prev",
13
 *            prevSet: "<<<",
14
 *            nextSet: ">>>",
15
 *            next:    "Next",
16
 *            last:    "Last"
17
 *        }} />
18
 * ```
19
 *
20
 * ## How it looks like
21
 * ```
22
 * First | Prev | ... | 6 | 7 | 8 | 9 | ... | Next | Last
23
 * ```
24
 *
25
 */
26
27
var React = require('react');
28
29
30
/**
31
 * ## Constants
32
 */
33
var BASE_SHIFT = 0
34
  , TITLE_SHIFT = 1
35
  , TITLES = {
36
  first: 'First',
37
  prev: '\u00AB',
38
  prevSet: '...',
39
  nextSet: '...',
40
  next: '\u00BB',
41
  last: 'Last'
42
};
43
44
/**
45
 * ## Constructor
46
 */
47
var Pager = React.createClass({
48
  displayName: "Pager",
49
  propTypes: {
50
    current: React.PropTypes.number.isRequired,
51
    total: React.PropTypes.number.isRequired,
52
    visiblePages: React.PropTypes.number.isRequired,
53
    titles: React.PropTypes.object,
54
55
    onPageChanged: React.PropTypes.func,
56
    onPageSizeChanged: React.PropTypes.func
57
  },
58
59
60
  /* ========================= HANDLERS =============================*/
61
  handleFirstPage: function () {
62
    if (this.isPrevDisabled()) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
63
    this.handlePageChanged(BASE_SHIFT);
64
  },
65
66
  handlePreviousPage: function () {
67
    if (this.isPrevDisabled()) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
68
    this.handlePageChanged(this.props.current - TITLE_SHIFT);
69
  },
70
71
  handleNextPage: function () {
72
    if (this.isNextDisabled()) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
73
    this.handlePageChanged(this.props.current + TITLE_SHIFT);
74
  },
75
76
  handleLastPage: function () {
77
    if (this.isNextDisabled()) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
78
    this.handlePageChanged(this.props.total - TITLE_SHIFT);
79
  },
80
81
  /**
82
   * Chooses page, that is one before min of currently visible
83
   * pages.
84
   */
85
  handleMorePrevPages: function () {
86
    var blocks = this.calcBlocks();
87
    this.handlePageChanged(blocks.current * blocks.size - TITLE_SHIFT);
88
  },
89
90
  /**
91
   * Chooses page, that is one after max of currently visible
92
   * pages.
93
   */
94
  handleMoreNextPages: function () {
95
    var blocks = this.calcBlocks();
96
    this.handlePageChanged((blocks.current + TITLE_SHIFT) * blocks.size);
97
  },
98
99
  handlePageChanged: function (el) {
100
    var handler = this.props.onPageChanged;
101
    if (handler) handler(el);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
102
  },
103
104
105
  /* ========================= HELPERS ==============================*/
106
  /**
107
   * Calculates "blocks" of buttons with page numbers.
108
   */
109
  calcBlocks: function () {
110
    var props = this.props
111
      , total = props.total
112
      , blockSize = props.visiblePages
113
      , current = props.current + TITLE_SHIFT
114
115
      , blocks = Math.ceil(total / blockSize)
116
      , currBlock = Math.ceil(current / blockSize) - TITLE_SHIFT;
117
118
    return {
119
      total: blocks,
120
      current: currBlock,
121
      size: blockSize
122
    };
123
  },
124
125
  isPrevDisabled: function () {
126
    return this.props.current <= BASE_SHIFT;
127
  },
128
129
  isNextDisabled: function () {
130
    return this.props.current >= ( this.props.total - TITLE_SHIFT );
131
  },
132
133
  isPrevMoreHidden: function () {
134
    var blocks = this.calcBlocks();
135
    return ( blocks.total === TITLE_SHIFT )
136
      || ( blocks.current === BASE_SHIFT );
137
  },
138
139
  isNextMoreHidden: function () {
140
    var blocks = this.calcBlocks();
141
    return ( blocks.total === TITLE_SHIFT )
142
      || ( blocks.current === (blocks.total - TITLE_SHIFT) );
143
  },
144
145
  visibleRange: function () {
146
    var blocks = this.calcBlocks()
147
      , start = blocks.current * blocks.size
148
      , delta = this.props.total - start
149
      , end = start + ( (delta > blocks.size) ? blocks.size : delta );
150
    return [start + TITLE_SHIFT, end + TITLE_SHIFT];
151
  },
152
153
154
  getTitles: function (key) {
155
    var pTitles = this.props.titles || {};
156
    return pTitles[key] || TITLES[key];
157
  },
158
159
  /* ========================= RENDERS ==============================*/
160
  render: function () {
161
    var titles = this.getTitles;
162
163
    return (
164
      React.createElement("nav", null,
165
        React.createElement("ul", {className: "pagination"},
166
          React.createElement(Page, {
167
            className: "btn-first-page",
168
            key: "btn-first-page",
169
            isDisabled: this.isPrevDisabled(),
170
            onClick: this.handleFirstPage
171
          }, titles('first')),
172
173
          React.createElement(Page, {
174
            className: "btn-prev-page",
175
            key: "btn-prev-page",
176
            isDisabled: this.isPrevDisabled(),
177
            onClick: this.handlePreviousPage
178
          }, titles('prev')),
179
180
          React.createElement(Page, {
181
            className: "btn-prev-more",
182
            key: "btn-prev-more",
183
            isHidden: this.isPrevMoreHidden(),
184
            onClick: this.handleMorePrevPages
185
          }, titles('prevSet')),
186
187
          this.renderPages(this.visibleRange()),
188
189
          React.createElement(Page, {
190
            className: "btn-next-more",
191
            key: "btn-next-more",
192
            isHidden: this.isNextMoreHidden(),
193
            onClick: this.handleMoreNextPages
194
          }, titles('nextSet')),
195
196
          React.createElement(Page, {
197
            className: "btn-next-page",
198
            key: "btn-next-page",
199
            isDisabled: this.isNextDisabled(),
200
            onClick: this.handleNextPage
201
          }, titles('next')),
202
203
          React.createElement(Page, {
204
            className: "btn-last-page",
205
            key: "btn-last-page",
206
            isDisabled: this.isNextDisabled(),
207
            onClick: this.handleLastPage
208
          }, titles('last'))
209
        )
210
      )
211
    );
212
  },
213
214
215
  /**
216
   * ### renderPages()
217
   * Renders block of pages' buttons with numbers.
218
   * @param {Number[]} range - pair of [start, from], `from` - not inclusive.
0 ignored issues
show
Documentation introduced by
The parameter range does not exist. Did you maybe forget to remove this comment?
Loading history...
219
   * @return {React.Element[]} - array of React nodes.
220
   */
221
  renderPages: function (pair) {
222
    var self = this;
223
224
    return range(pair[0], pair[1]).map(function (el, idx) {
225
      var current = el - TITLE_SHIFT
226
        , onClick = self.handlePageChanged.bind(null, current)
227
        , isActive = (self.props.current === current);
228
229
      return (React.createElement(Page, {
230
        key: idx, isActive: isActive,
231
        className: "btn-numbered-page",
232
        onClick: onClick
233
      }, el));
234
    });
235
  }
236
});
237
238
239
var Page = React.createClass({
240
  displayName: "Page",
241
  render: function () {
242
    var props = this.props;
243
    if (props.isHidden) return null;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
244
245
    var baseCss = props.className ? props.className + ' ' : ''
246
      , css = baseCss
247
      + (props.isActive ? 'active' : '')
248
      + (props.isDisabled ? ' disabled' : '');
249
250
    return (
251
      React.createElement("li", {key: this.props.key, className: css},
252
        React.createElement("a", {onClick: this.props.onClick}, this.props.children)
253
      )
254
    );
255
  }
256
});
257
258
259
function range(start, end) {
260
  var res = [];
261
  for (var i = start; i < end; i++) {
262
    res.push(i);
263
  }
264
265
  return res;
266
}
267
268
if ('undefined' !== typeof module) {
269
  module.exports = Pager;
270
}
271